﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using Budget.Silver.IncomeServiceReference;
using Budget.Silver.Core.Extensions;
using Budget.Silver.Core.Messages;
using GalaSoft.MvvmLight.Command;
using MvvmValidation;

namespace Budget.Silver.ViewModel
{
    public class IncomeViewModel : ValidatableViewModel
    {
        #region Members data

        private readonly IncomeServiceClient service;

        private decimal amount;
        private ObservableCollection<IncomeTypeData> incomeTypes;
        private ObservableCollection<IncomeOrderData> incomeOrders;
        private string selectedIncomeTypeName;
        private DateTime selectedDate;
        private string amountString;

        #endregion

        private void Clear()
        {
            amount = 0;
            amountString = string.Empty;
            RaisePropertyChanged(() => AmountString);
            RaisePropertyChanged(() => IsReady);
        }

        private IncomeOrderData CreateIncomeOrder(IncomeTypeData incomeType)
        {
            return new IncomeOrderData
                       {
                           CreationDate = SelectedDate,
                           Amount = amount,
                           IncomeType = incomeType
                       };
        }

        #region Completed methods

        private void AddCompletedEvents()
        {
            service.AddIncomeOrderCompleted += ServiceOnAddIncomeOrderCompleted;
            service.GetAllIncomeTypesCompleted += ServiceOnGetAllIncomeTypesCompleted;
            service.GetAllIncomeOrdersCompleted += ServiceOnGetAllIncomeOrdersCompleted;
        }

        private void ServiceOnAddIncomeOrderCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                Clear();
            }
            else
            {
                MessengerInstance.Send(new ExceptionMessage(e.Error));
            }

            MessengerInstance.Send(new BusyMessage(false));
        }

        private void ServiceOnGetAllIncomeOrdersCompleted(object sender, GetAllIncomeOrdersCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                IncomeOrders = e.Result.SortByDescending(o => o.CreationDate);
            }
            else
            {
                MessengerInstance.Send(new ExceptionMessage(e.Error));
            }

            MessengerInstance.Send(new BusyMessage(false));
        }

        private void ServiceOnGetAllIncomeTypesCompleted(object sender, GetAllIncomeTypesCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                IncomeTypes = e.Result;

                service.GetAllIncomeOrdersAsync();
            }
            else
            {
                MessengerInstance.Send(new ExceptionMessage(e.Error));
                MessengerInstance.Send(new BusyMessage(false));
            }
        }

        #endregion

        #region Commands

        private void CreateCommands()
        {
            PressEnter = new RelayCommand<KeyEventArgs>(PressEnterCommand);
            SelectIncomeType = new RelayCommand<SelectionChangedEventArgs>(SelectIncomeTypeCommand);
            AddIncomeOrder = new RelayCommand(AddIncomeOrderCommand);
        }

        private void PressEnterCommand(KeyEventArgs e)
        {
            if (e.Key == Key.Enter && IsReady)
            {
                AddIncomeOrderCommand();
            }
        }

        private void AddIncomeOrderCommand()
        {
            IncomeTypeData incomeType;

            if (IncomeTypes.Any(type => type.Name == SelectedIncomeTypeName))
            {
                incomeType = IncomeTypes.First(type => type.Name == SelectedIncomeTypeName);
            }
            else
            {
                incomeType = new IncomeTypeData { Name = SelectedIncomeTypeName };
                IncomeTypes.Add(incomeType);
                RaisePropertyChanged(() => IncomeTypes);
            }

            var order = CreateIncomeOrder(incomeType);
            IncomeOrders.Insert(0, order);

            MessengerInstance.Send(new BusyMessage(true));

            service.AddIncomeOrderAsync(order);
        }

        private void SelectIncomeTypeCommand(SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                var incomeType = e.AddedItems[0] as IncomeTypeData;

                if (incomeType != null)
                {
                    SelectedIncomeTypeName = incomeType.Name;
                }
            }
        }

        #endregion

        #region Validation

        protected override void AddValidationRules()
        {
            Validator.AddRequiredRule(() => AmountString, "Required!");
            Validator.AddRule(() => AmountString, () => RuleResult.Assert(AmountString.TryParseToDecimal(out amount), "Is not a number!"));
            Validator.AddRule(() => AmountString, () => RuleResult.Assert(amount > 0, "Incorrect amount!"));
        }

        #endregion

        #region Binding data

        public ICommand SelectIncomeType { get; private set; }

        public ICommand AddIncomeOrder { get; private set; }

        public ICommand PressEnter { get; private set; }

        public ObservableCollection<IncomeTypeData> IncomeTypes
        {
            get { return incomeTypes; }
            set
            {
                if (!Equals(incomeTypes, value))
                {
                    incomeTypes = value;

                    RaisePropertyChanged(() => IncomeTypes);
                }
            }
        }

        public ObservableCollection<IncomeOrderData> IncomeOrders
        {
            get { return incomeOrders; }
            private set
            {
                if (!Equals(incomeOrders, value))
                {
                    incomeOrders = value;

                    RaisePropertyChanged(() => IncomeOrders);
                }
            }
        }

        public string SelectedIncomeTypeName
        {
            get { return selectedIncomeTypeName; }
            set
            {
                if (!Equals(selectedIncomeTypeName, value))
                {
                    selectedIncomeTypeName = value;

                    RaisePropertyChanged(() => SelectedIncomeTypeName);
                }
            }
        }

        public DateTime SelectedDate
        {
            get { return selectedDate; }
            set
            {
                if (!Equals(selectedDate, value))
                {
                    selectedDate = value;

                    RaisePropertyChanged(() => SelectedDate);
                }
            }
        }

        public Boolean IsReady
        {
            get { return !string.IsNullOrEmpty(SelectedIncomeTypeName) && amount > 0; }
        }

        public String AmountString
        {
            get { return amountString; }
            set
            {
                if (!Equals(amountString, value))
                {
                    amountString = value;

                    amount = 0;
                    if (!amountString.TryParseToDecimal(out amount))
                    {
                        RaisePropertyChanged(() => IsReady);
                    }

                    ValidateAll();

                    amount = amountString.ParseToDecimal();

                    RaisePropertyChanged(() => IsReady);
                }
            }
        }

        #endregion

        public IncomeViewModel(IncomeServiceClient service)
        {
            this.service = service;

            SelectedDate = DateTime.Now;

            CreateCommands();
            AddCompletedEvents();

            MessengerInstance.Send(new BusyMessage(true));
            
            service.GetAllIncomeTypesAsync();
        }
    }
}